module lpuart_bclk_gen(
  input          uart_clk,    
  input          uart_rst_n, 

  input  [3:0]   clock_psc,
  input  [19:0]  baud_rate,
  input          stop_bclk,
  input          baud_clk_start,

  output reg     baud_clk,
  output reg     baud_clk_offset
);

reg [7:0] clock_psc_devider;
always@(*)begin
  clock_psc_devider = 8'd0;
  case(clock_psc)
    4'b0000: clock_psc_devider = 8'd0;
    4'b0001: clock_psc_devider = 8'd1;
    4'b0010: clock_psc_devider = 8'd3;
    4'b0011: clock_psc_devider = 8'd5;
    4'b0100: clock_psc_devider = 8'd7;
    4'b0101: clock_psc_devider = 8'd9;
    4'b0110: clock_psc_devider = 8'd11;
    4'b0111: clock_psc_devider = 8'd15;
    4'b1000: clock_psc_devider = 8'd31;
    4'b1001: clock_psc_devider = 8'd63;
    4'b1010: clock_psc_devider = 8'd127;
    4'b1011: clock_psc_devider = 8'd255;
    default: clock_psc_devider = 8'd0;
  endcase
end

// input clock prescaler
reg [7:0] psc_cnt;
reg       latch_psc_cnt_bit0;

wire load_psc_cnt = (psc_cnt == 8'h0) || stop_bclk || baud_clk_start;
always@(posedge uart_clk, negedge uart_rst_n)begin:PSC_CNT
  if(!uart_rst_n)
    psc_cnt <= 8'h0;
  else if(clock_psc_devider == 8'h0)
    psc_cnt <= 8'h0;
  else if(load_psc_cnt)
    psc_cnt <= clock_psc_devider;
  else
    psc_cnt <= psc_cnt - 1'b1;
end

always@(posedge uart_clk, negedge uart_rst_n)begin
  if(!uart_rst_n)
    latch_psc_cnt_bit0 <= 1'b0;
  else
    latch_psc_cnt_bit0 <= psc_cnt[0];
end

// baud rate
wire [11:0] baud_rate_divm      = baud_rate[19:8];
wire [11:0] baud_rate_divm_half = (baud_rate[19:8]>>1);
wire [3:0]  baud_rate_divf = baud_rate[7:4];

reg  [11:0] baud_mcnt;
reg         latch_baud_mcnt_bit0;

reg  [3:0]  latch_div_f;
reg  [3:0]  baud_f_add;
wire [4:0]  baud_f_add_comb;
reg         div_m_carry;

wire update_baud_rate_cnt = stop_bclk || baud_clk_start;
always@(posedge uart_clk, negedge uart_rst_n) begin
  if(!uart_rst_n)
    latch_div_f <= 4'h0;
  else if (update_baud_rate_cnt)
    latch_div_f <= baud_rate_divf;  
end

assign baud_f_add_comb = {1'b0,baud_f_add} + {1'b0,latch_div_f};
  
always@(posedge uart_clk, negedge uart_rst_n) begin:DAUD_F_ADD
  if(!uart_rst_n)
    baud_f_add <= 4'h0;    
  else if (update_baud_rate_cnt)  
    baud_f_add <= 4'h0;  
  else if (baud_clk)   
    baud_f_add <= baud_f_add_comb[3:0];
end

always@(posedge uart_clk, negedge uart_rst_n) begin
  if(!uart_rst_n)
    div_m_carry <= 1'b0;
  else if (update_baud_rate_cnt)   
    div_m_carry <= 1'b0;
  else if (baud_clk)   
    div_m_carry <= baud_f_add_comb[4];
end

always@(posedge uart_clk, negedge uart_rst_n) begin
  if(!uart_rst_n)
    latch_baud_mcnt_bit0 <= 1'b0;
  else
    latch_baud_mcnt_bit0 <= baud_mcnt[0];
end
  
always@(posedge uart_clk, negedge uart_rst_n) begin:DAUD_MCNT
  if(!uart_rst_n)
    baud_mcnt <= 12'h03;
  else if (update_baud_rate_cnt)
    baud_mcnt <= baud_rate_divm;
  else if (baud_mcnt == 12'h01)
    baud_mcnt <= div_m_carry ? (baud_rate_divm + 1'b1) : baud_rate_divm;
  else   
    baud_mcnt <= baud_mcnt - 1'b1;
end

wire next_bclk = ((clock_psc_devider == 8'h0) || (~latch_psc_cnt_bit0 && (psc_cnt == 8'h01))) && 
                 (~latch_baud_mcnt_bit0 && (baud_mcnt == 12'h01));  
wire next_bclk_offset = ((clock_psc_devider == 8'h0) || (~latch_psc_cnt_bit0 && (psc_cnt == 8'h01))) && 
                        (latch_baud_mcnt_bit0 && (baud_mcnt == baud_rate_divm_half)) &&
                        (~baud_clk_start);
 
always@(posedge uart_clk, negedge uart_rst_n) begin:BAUT_CLOCK_PROC
  if(!uart_rst_n)begin
    baud_clk        <= 1'b0;
    baud_clk_offset <= 1'b0;
  end
  else begin
    baud_clk        <= next_bclk;
    baud_clk_offset <= next_bclk_offset;
  end
end

endmodule
